/**
  ******************************************************************************
  * @file           
  * @author         古么宁
  * @brief          //在此处编写应用层的 ASDU 处理
  *                 依赖 IEC_ASDU.c
  ******************************************************************************
  *
  * COPYRIGHT(c) 2019 GoodMorning
  *
  ******************************************************************************
  */
/* Includes ---------------------------------------------------*/
#include <string.h>

#include "iec10x.h"
#include "iec10x_link.h"
#include "iec10x_event.h"

#include "ASDUTYP.h"
#include "SIQ.h"
#include "DIQ.h"
#include "NVA.h"
#include "STD.h"
#include "COT.h"

/* Private types ------------------------------------------------------------*/

struct iecslv_event {
	struct iec_list   node      ;
	uint32_t          infoaddr  ;
	unsigned char     type      ;
	unsigned char     data[1]   ;
};

struct iecmst_event {
	struct iec_list   node    ;
	mst_func_t        func    ;
	uint8_t           arg[1]  ;
};

/* Private macro ------------------------------------------------------------*/
/* Private variables --------------------------------------------------------*/
/* Global  variables --------------------------------------------------------*/
/* Private function prototypes ----------------------------------------------*/





static inline int event_type_size(uint8_t type)
{	
	int typsize;
	switch (type) {
	case M_SP_NA_1 :
		typsize = sizeof(SIQ0T_t);
		break;
	case M_SP_TA_1 :
		typsize = sizeof(SIQ24T_t);
		break;
	case M_DP_NA_1:
		typsize = sizeof(DIQ0T_t);
		break;
	case M_DP_TA_1:
		typsize = sizeof(DIQ24T_t);
		break;
	case M_ME_NC_1:
		typsize = sizeof(STD0T_t);
		break;
	case M_ME_TC_1:
		typsize = sizeof(STD24T_t);
		break;
	case M_ME_TF_1:
		typsize = sizeof(STD56T_t);
		break;
	default: 
		return 0;
	}

	return typsize;
}



/**
  * @brief    从机处理一个 soe 事件。
  * @param    bus      : 发生突发事件的总线 
  * @return   don't care
*/
void iec10xslv_event_pro(iec10x_t * bus)
{
	IEC_INDEX(bus->causelen,bus->cmnaddrlen);
	uint8_t * databuf = ASDU_TXBUFF(bus);
	uint8_t * type = &databuf[IEC_TYPE];
	uint8_t * soe  = &databuf[IEC_INFO];
	uint8_t * nums = &databuf[IEC_QUALIFIER];
	uint8_t   max  = 0;
	int       typsize;

	struct iec_list  * pos , *next , * end ;
	struct iecslv_event  * event = NULL;

	databuf[IEC_TYPE]          = 0                       ;
	databuf[IEC_QUALIFIER]     = 0x00                    ;
	databuf[IEC_COT]           = COT_BURST               ;
	databuf[IEC_COT+1]         = bus->connectaddr & 0xff ;
	databuf[IEC_COMMON_ADDR]   = bus->cmnaddr     & 0xff ;
	databuf[IEC_COMMON_ADDR+1] = bus->cmnaddr     >> 8   ;

	/* 获取链表第一个元素的类型，计算当前包可以容纳最多的结构体个数 */
	event   = container_of(bus->event.next,struct iecslv_event,node);
	*type   = event->type ;
	typsize = event_type_size(event->type);
	max     = bus->pktmax / (typsize + bus->infoaddrlen) ;
	if (max > 127) {
		max = 127;
	}

	iec_sys_mutex_lock(&bus->mtx);  
	pos  = NULL;
	next = bus->event.next ; 
	end  = bus->event.prev ;
	while((pos != end) && (*nums < max)) {
		pos   = next ;
		next  = pos->next ;
		event = container_of(pos,struct iecslv_event,node);
		if (event->type == *type) {
			for (int i = 0 ; i < bus->infoaddrlen ; ++i) {
				*soe++ = (event->infoaddr >> (i * 8)) & 0x00ff;
			}
			memcpy(soe, event->data , typsize) ;
			soe += typsize ;
			iec_list_del(&event->node);
			iec_free(event);
			++(*nums) ;
		}
	}

	iec_sys_mutex_unlock(&bus->mtx);  

	/* 发送当前包数据 */
	if (bus->mode & IEC_101) {
		uint8_t lnkctrl = 0 ;
		if (bus->mode & IEC_BALANCE) {
			int FCB = (bus->iec101.FCB == 0) ;
			bus->iec101.FCB = FCB;
			lnkctrl  = IEC_CTRL(0,1,FCB,1,SLAVE_OK);
			lnkctrl |= (bus->mode & IEC_DIRECT)    ;
		}
		else {
			lnkctrl = IEC_CTRL(0,0,0,0,SLAVE_OK);
		}
		iec101_protocol_send(bus,databuf,soe-databuf,lnkctrl); 
	}
	else {
		iec104_protocol_send(bus,databuf,soe-databuf,0); 
	}
}


/**
  * @brief    从机产生一个 soe 事件。
  * @param    bus      : 发生突发事件的总线 
  * @param    type     : 事件类型， @see slv_event_t
  * @param    infoaddr : 事件信息体地址
  * @param    data     : 事件信息体具体内容,如 SIQ24T_t / DIQ24T_t
  * @return   成功返回0 ， 否则返回其他
*/
int iec10xslv_event(iec10x_t * bus ,slv_event_t type, uint32_t infoaddr,void * data)
{
	struct iecslv_event * newevent ;
	int typsize;
	
	if (!bus->connected) {
		return -1;
	}

	typsize = event_type_size(type);
	if (typsize == 0) {
		return -1;
	}

	newevent = iec_malloc(typsize + sizeof(struct iecslv_event));
	if (newevent == NULL) {
		return -1;
	}

	newevent->infoaddr  = infoaddr ;
	newevent->type      = type ;
	memcpy(newevent->data,data,typsize);

	/* 尾插法加入链路末端 */
	iec_sys_mutex_lock(&bus->mtx);  
	iec_list_add_tail(&newevent->node,&bus->event);
	iec_sys_mutex_unlock(&bus->mtx); 
	return 0;
}




/**
  * @brief    主机处理一个 soe 事件。
  * @param    bus      : 发生突发事件的总线 
  * @return   don't care
*/
void iec10xmst_event_pro(iec10x_t * bus)
{
	struct iecmst_event * event = NULL ;
	struct iec_list  * node = bus->event.next;

	if (node != &bus->event) {
		event = container_of(node, struct iecmst_event, node);
		/* 上锁互斥量，防止读取的时候 bus->event 被修改 */
		iec_sys_mutex_lock(&bus->mtx);
		iec_list_del(node);
		iec_sys_mutex_unlock(&bus->mtx);
		event->func(bus,event->arg);
		iec_free(event);
	}
}


int iec10xmst_event(iec10x_t *bus , mst_func_t func , void * arg , int argsize)
{
	struct iecmst_event * newevent ;
	newevent = iec_malloc(sizeof(struct iecmst_event) + argsize);
	if (!newevent) {
		return -1;
	}

	newevent->node.next = NULL ;
	newevent->func      = func ;
	if (argsize && arg){
		memcpy(newevent->arg, arg, argsize) ;
	}

	iec_sys_mutex_lock(&bus->mtx);
	iec_list_add_tail(&newevent->node,&bus->event);
	iec_sys_mutex_unlock(&bus->mtx);
	return 0;
}



void iec10x_event_deinit(iec10x_t *bus)
{
	struct iec_list  * node = bus->event.next;
	struct iecmst_event * mstevent ;
	struct iecslv_event * slvevent ;

	init_iec_list(&bus->event);

	if (bus->mode & IEC_MASTER) {
		while(node != &bus->event) {
			mstevent = container_of(node, struct iecmst_event,node);
			node = node->next;
			iec_free(mstevent);
		}
	}
	else {
		while(node != &bus->event) {
			slvevent = container_of(node,struct iecslv_event,node);
			node = node->next;
			iec_free(slvevent);
		}
	}
}
